// 日志器模块-对前面的模块进行整合
#ifndef __MY_LOGGER__
#define __MY_LOGGER__

#include <unistd.h>
#include <stdarg.h>
#include <iostream>
#include <unordered_map>
#include <atomic>
#include <mutex>

#include "AsyncLooper.hpp"

#include "util.hpp"
#include "message.hpp"
#include "format.hpp"
#include "sink_expand.hpp"
#include "sink.hpp"
#include "level.hpp"
namespace ken_log
{
    // 抽象出一个日志器基类，派生出同步日志器和异步日志器，差别就是落地方式的不同，一个是放到内存，一个是磁盘
    class Logger
    {
    public:
        // 使用指针方便后面进行管理
        using ptr = std::shared_ptr<Logger>;
        // 在构造函数中对成员变量初始化
        Logger(const std::string &logger_name, LogLevel::value level,
               Formatter::ptr &formatter, std::vector<LogSink::ptr> &sinks)
            : _logger_name(logger_name), _limit_level(level), _formatter(formatter), _sinks(sinks.begin(), sinks.end())
        {
        }
        // 抽象接口完成实际的落地输出-不同的日志器会有不同的实际落地方式
        void debug(const std::string &file, size_t line, std::string fmt, ...)
        {
            // 日志等级使用的是原子操作，避免了竞争和互斥
            // 1、 判断当前日志是否达到了日志输出等级
            if (LogLevel::value::DEBUG < _limit_level)
            {
                // 没达到直接退出
                return;
            }
            // 2、对fmt格式化字符串和不定参进行字符串组织得到日志消息的字符串
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            // 如果ret等于-1代表格式化字符串过程出错
            if (ret == -1)
            {
                std::cout << "vasprintf failed\n";
                return;
            }
            // 到这里res就包含了格式化组织之后的字符串
            va_end(ap);
            serialize(LogLevel::value::DEBUG, file, line, res);
            free(res); // res指向的空间需要手动释放
        }

        void info(const std::string &file, size_t line, std::string fmt, ...)
        {
            // 日志等级使用的是原子操作，避免了竞争和互斥
            // 1、 判断当前日志是否达到了日志输出等级
            if (LogLevel::value::INFO < _limit_level)
            {
                // 没达到直接退出
                return;
            }
            // 2、对fmt格式化字符串和不定参进行字符串组织得到日志消息的字符串
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            // 如果ret等于-1代表格式化字符串过程出错
            if (ret == -1)
            {
                std::cout << "vasprintf failed\n";
                return;
            }
            // 到这里res就包含了格式化组织之后的字符串
            va_end(ap);
            serialize(LogLevel::value::INFO, file, line, res);
            free(res); // res指向的空间需要手动释放
        }
        void warn(const std::string &file, size_t line, std::string fmt, ...)
        {
            // 日志等级使用的是原子操作，避免了竞争和互斥
            // 1、 判断当前日志是否达到了日志输出等级
            if (LogLevel::value::WARN < _limit_level)
            {
                // 没达到直接退出
                return;
            }
            // 2、对fmt格式化字符串和不定参进行字符串组织得到日志消息的字符串
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            // 如果ret等于-1代表格式化字符串过程出错
            if (ret == -1)
            {
                std::cout << "vasprintf failed\n";
                return;
            }
            // 到这里res就包含了格式化组织之后的字符串
            va_end(ap);
            serialize(LogLevel::value::WARN, file, line, res);
            free(res); // res指向的空间需要手动释放
        }
        void error(const std::string &file, size_t line, std::string fmt, ...)
        {
            // 日志等级使用的是原子操作，避免了竞争和互斥
            // 1、 判断当前日志是否达到了日志输出等级
            if (LogLevel::value::ERROR < _limit_level)
            {
                // 没达到直接退出
                return;
            }
            // 2、对fmt格式化字符串和不定参进行字符串组织得到日志消息的字符串
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            // 如果ret等于-1代表格式化字符串过程出错
            if (ret == -1)
            {
                std::cout << "vasprintf failed\n";
                return;
            }
            // 到这里res就包含了格式化组织之后的字符串
            va_end(ap);
            serialize(LogLevel::value::ERROR, file, line, res);
            free(res); // res指向的空间需要手动释放
        }
        void fatal(const std::string &file, size_t line, std::string fmt, ...)
        {
            // 日志等级使用的是原子操作，避免了竞争和互斥
            // 1、 判断当前日志是否达到了日志输出等级
            if (LogLevel::value::FATAL < _limit_level)
            {
                // 没达到直接退出
                return;
            }
            // 2、对fmt格式化字符串和不定参进行字符串组织得到日志消息的字符串
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            // 如果ret等于-1代表格式化字符串过程出错
            if (ret == -1)
            {
                std::cout << "vasprintf failed\n";
                return;
            }
            // 到这里res就包含了格式化组织之后的字符串
            va_end(ap);
            serialize(LogLevel::value::FATAL, file, line, res);
            free(res); // res指向的空间需要手动释放
        }
        // 返回日志器名称,防止外界对其修改，使用const引用
        const std::string &name()
        {
            return _logger_name;
        }

    protected:
        void serialize(LogLevel::value level, const std::string &file, size_t line, char *str)
        {
            // 3、构造LogMsg对象
            LogMsg msg(level, file, line, str, _logger_name);
            // 4、通过格式化工具对msg消息进行格式化处理，得到格式化之后的字符串
            std::stringstream ss;
            _formatter->format(ss, msg);
            // 5、进行日志落地，调用具体的log接口
            log(ss.str().c_str(), ss.str().size());
        }

    protected:
        // 抽象接口完成实际的落地输出-不同的日志器会有不同的实际落地方式
        virtual void log(const char *data, size_t len) = 0;

    protected:
        Formatter::ptr _formatter; // 格式化模块对象
        // 指针数组，通过父类指针指向子类对象进行访问
        std::vector<LogSink::ptr> _sinks;          // 落地模块对象数组，可能有多个日志落地方向
        std::atomic<LogLevel::value> _limit_level; // 日志输出限制等级，头文件atomic
        std::mutex _mutex;                         // 互斥锁保证日志输出是线程安全的
        std::string _logger_name;                  // 日志器名称
    };

    // 派生出同步日志器
    class SyncLogger : public Logger
    {
    public:
        SyncLogger(const std::string &logger_name, LogLevel::value level,
                   Formatter::ptr &formatter, std::vector<LogSink::ptr> &sinks)
            : Logger(logger_name, level, formatter, sinks)
        {
        }

    protected:
        // 同步日志器是将日志直接通过落地模块句柄进行日志落地
        void log(const char *data, size_t len) override
        {
            // 加锁，使用锁的管理器将锁管理起来，自动对_mutex加锁和解锁
            std::unique_lock<std::mutex> lock(_mutex);
            if (_sinks.empty())
            {
                return;
            }
            // 如果不为空，遍历落地方向的数组
            for (auto &sinks : _sinks)
            {
                // 使用log进行日志落地
                sinks->log(data, len);
            }
        }
    };

    // 日志器建造者类
    // 日志器枚举类
    enum class LoggerType
    {
        LOGGER_SYNC,
        LOGGER_ASYNC,
    };

    class LoggerBuilder
    {
        // 第一步：设置日志器类型；第二步：将不同类型日志器的创建放到同一个日志器建造者类中完成
    public:
        LoggerBuilder()
            : _logger_type(LoggerType::LOGGER_SYNC) // 默认日志器类型
              ,
              _limit_level(LogLevel::value::DEBUG) // 默认输出限制等级
              ,
              _looper_type(AsyncLooper::AsyncType::ASYNC_SAFE) // 默认是安全模式
        {
        }
        // 启动一个非安全的异步操作
        void buildEnableUnSafeAsync()
        {
            _looper_type = AsyncLooper::AsyncType::ASYNC_UNSAFE;
        }
        // 建造日志器类型
        void buildLoggerType(LoggerType type)
        {
            _logger_type = type;
        }
        // 日志器名称
        void buildLoggerName(const std::string &name)
        {
            _logger_name = name;
        }
        // 建造日志器等级
        void buildLoggerLevel(LogLevel::value level)
        {
            _limit_level = level;
        }
        // 建造一个日志格式化器，设置日志输出规则
        void buildFormatter(const std::string &pattern)
        {
            // 构造一个Formatter对象，通过智能指针来对这个对象进行管理
            _formatter = std::make_shared<Formatter>(pattern);
        }
        // 日志落地方向不同所以使用模板参数
        template <class SinkType, class... Args>
        void buildSink(Args &&...args)
        {
            LogSink::ptr psink = SinkFactory::create<SinkType>(std::forward<Args>(args)...);
            // 添加到日志器数组中
            _sinks.push_back(psink);
        }
        // 创建一个添加一个，创建完了来一个build完成日志器的创建
        virtual Logger::ptr build() = 0;

    protected:
        AsyncLooper::AsyncType _looper_type; // 异步器工作模式，是否是安全状态
        LoggerType _logger_type;             // 日志器类型
        std::string _logger_name;            // 日志器名称
        LogLevel::value _limit_level;        // 日志等级,这里不对他进行操作，所以不需要设置原子类型
        Formatter::ptr _formatter;           // 日志格式化对象
        std::vector<LogSink::ptr> _sinks;    // 日志落地方向
    };
    class LocalLoggerBuilder : public LoggerBuilder
    {
    public:
        Logger::ptr build() override
        {
            // 构造对象
            assert(_logger_name.empty() == false); // 判断是否有日志器名称
            // 必须要有formatter,如果为空构造一个
            if (_formatter.get() == nullptr)
            {
                _formatter = std::make_shared<Formatter>();
            }
            // 如果落地方向也为空,默认添加一个标准输出的落地器
            if (_sinks.empty())
            {
                buildSink<StdoutSink>();
            }
            // 如果是异步日志器
            if (_logger_type == LoggerType::LOGGER_ASYNC)
            {
                return std::make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sinks, _looper_type);
            }
            // 如果是同步日志器就返回同步日志器
            return std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks);
        }
    };

}
#endif